<template>
  <div class="history-track">
    <div class="back-btn" @click="hideHistoryTrack">
      <el-icon>
        <Back />
      </el-icon>
      <span class="title">{{ params.name }} - 下井轨迹</span>
    </div>
    <div class="track-steps-container" v-loading="loading">
      <el-steps class="track-steps" direction="vertical" space="80px" :active="active" finish-status="finish"
        align-center>
        <template v-for="item in trackData" :key="item.id">
          <el-step :title="(item.jzName || '--') + `(${item.distanceM}米)`">
            <template #description>
              <div class="time">{{ item.inJzTime }}</div>
            </template>
          </el-step>
        </template>
      </el-steps>
    </div>

    <!-- 时间轴组件容器 -->
    <div class="time-line" v-if="trackData.length">
      <!-- 进度条容器，绑定点击、鼠标移动和鼠标离开事件 -->
      <div class="progress-box" @click="seek($event)" @mousemove="showTimeOnHover($event)" @mouseleave="hideTimeLabel">
        <!-- 进度条，根据 progress 值动态设置宽度 -->
        <div class="progress" :style="{ width: progress * 100 + '%' }">
          <!-- 显示当前时间的标签 -->
          <div class="time-label">
            <span>{{ currentTime }}</span>
          </div>
        </div>
        <!-- 动态设置样式，鼠标悬停时显示对应位置的时间 -->
        <div class="hover-time" v-if="showHoverTime" :style="{ left: hoverX + 'px' }">
          <span>{{ hoverTime }}</span>
        </div>
        <!-- 显示开始时间 -->
        <div class="start-time">{{ dayjs(trackData[0].inJzTime).format('YYYY-MM-DD HH:mm') }}</div>
        <!-- 显示结束时间 -->
        <div class="end-time">{{ dayjs(trackData[trackData.length - 1].inJzTime).format('YYYY-MM-DD HH:mm') }}</div>
      </div>

      <!-- 播放/暂停按钮 -->
      <el-icon class="time-line-btn" @click="playOrPause">
        <!-- 未播放时显示播放图标 -->
        <VideoPlay v-if="!playing" />
        <!-- 播放时显示暂停图标 -->
        <VideoPause v-else />
      </el-icon>

      <!-- 速度选择器 -->
      <div class="speed">
        <el-select v-model="speed" placeholder="Select" style="width: 80px" @change="speedChange">
          <!-- 遍历选项数组，生成下拉选项 -->
          <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
        </el-select>
      </div>
    </div>
  </div>
</template>
<script setup lang="ts">
import { computed, createVNode, onMounted, onUnmounted, ref, render, shallowRef } from 'vue'
import { Back, VideoPause, VideoPlay } from '@element-plus/icons-vue'
import { getPersonTrackData } from '@/api/person'
import { getPath } from '@/utils/getPath'
import { useActiveObject } from '@/store/activeObject'
import { CSS2DObject, GLTFLoader } from 'three/examples/jsm/Addons.js'
import { AnimationAction, AnimationMixer, CatmullRomCurve3, Clock } from 'three'
import { gsap } from 'gsap'
import { useFocusObject } from '@/utils/useFocusObject'
import { useViewportStore } from '@/store/viewport'
import { storeToRefs } from 'pinia'
import useAnimationFrame from '@/hooks/useAnimationFrame'
import { findClosestT, findTrackPath, mergeCurvesSequentially } from '@/utils/findTrackPath'
import dayjs from 'dayjs'
import BTrack from '@/components/b-track.vue'

const emit = defineEmits(['hideHistoryTrack'])
function hideHistoryTrack () {
  emit('hideHistoryTrack')
  setIsHistoryTracking(false)
}

const props = defineProps({
  params: {
    type: Object,
    default: () => { }
  }
})

const loader = new GLTFLoader()
const BASE_URL = import.meta.env.BASE_URL
function loadGLTFModel () {
  return new Promise((resolve, reject) => {
    loader.load(
      BASE_URL + 'model/people.glb',
      (gltf) => {
        resolve(gltf)
      },
      undefined,
      (error) => {
        reject(error)
      }
    )
  })
}

const loading = ref(false)
const { setIsHistoryTracking } = useActiveObject()
const { focusObject } = useFocusObject()
const viewportStore = useViewportStore()
const { scene, curveNetwork, baseVec } = storeToRefs(viewportStore)
const clock = new Clock();
const timeline = gsap.timeline() // 创建时间线
const mixer = shallowRef<AnimationMixer>() // 创建动画混合器
const clip = shallowRef<AnimationAction>() // 动画剪辑
const peopleModel = shallowRef()
const label = shallowRef()
onMounted(async () => {
  loading.value = true
  const { data } = await getPersonTrackData(props.params.inTime, props.params.peopleNo)
  // const { data } = await axios.get('/test.json')
  setIsHistoryTracking(true)
  let dataFilters = []
  for (let i = 0; i < data.length; i++) {
    const item = data[i];
    item.jzPoint = window.jzPoints[item.jzNo];
    if (!item.jzPoint) {
      console.warn('未找到数据', item) // 打印警告信息
      continue; // 跳过当前循环
    }
    const road = getPath(item.jzPoint);
    if (!road) {
      console.warn('未找到道路', item) // 打印警告信息
      continue; // 跳过当前循环
    }
    item.curve = road.geometry.parameters.path;
    item.uuid = road.uuid;
    dataFilters.push(item)
  }
  trackData.value = dataFilters
  currentTrack.value = trackData.value[0]
  const { scene: model, animations } = await loadGLTFModel()
  model.scale.set(3,3,3)
  peopleModel.value = model
  loading.value = false
  scene.value.add(peopleModel.value) // 添加模型到场景中
  mixer.value = new AnimationMixer(peopleModel.value);
  clip.value = mixer.value.clipAction(animations[0])

  label.value = createdTracklLabel()
  peopleModel.value.add(label.value)

  const steps = document.querySelectorAll('.el-step')
  try {
    const arr = mergeCurvesSequentially(trackData.value)
    const resultPath = findTrackPath(arr, curveNetwork.value.graph, curveNetwork.value.connectionMap);
    console.log(resultPath);
    for (let i = 0; i < resultPath.length; i++) {
      if (resultPath[i].path.length < 2) {
        timeline.to(null, {
          duration: resultPath[i].time,
          onComplete: () => {
            console.log(`${i}动画完成！`);
            active.value = i + 2
            steps[i].scrollIntoView({ behavior: 'smooth', block: "start", inline: "nearest" })
          }
        })
        continue
      }
      const curve = new CatmullRomCurve3(resultPath[i].path);
      const tA = findClosestT(curve, resultPath[i].path[0])
      const tB = findClosestT(curve, resultPath[i].path[resultPath[i].path.length - 1])
      const obj = { t: tA }; // 中间变量存储当前t值
      timeline.to(obj, {
        t: tB,
        duration: resultPath[i].time,
        ease: "linear",
        onUpdate: () => {
          // 根据当前t值更新物体位置
          const position = curve.getPoint(obj.t).sub(baseVec.value);
          peopleModel.value.position.copy(position);

          const tangent = curve.getTangent(obj.t);
          peopleModel.value.lookAt(position.clone().add(tangent));
          focusObject(peopleModel.value, 200, 0.3)
        },
        onComplete: () => {
          console.log(`${i}动画完成！`);
          steps[i].scrollIntoView({ behavior: 'smooth', block: "start", inline: "nearest" })
          active.value = i + 2
          currentTrack.value = trackData.value[i + 1]
        }
      })
      timeline.pause()
      timeline.eventCallback("onComplete", () => {
        isEnd = true
        stopPlay()
      })
      timeline.eventCallback("onUpdate", () => {
        progress.value = timeline.progress()
      })
    }
  } catch (error) {
    console.warn('findContinuousPath error', error)
  }
  useAnimationFrame(() => {
    const delta = clock.getDelta();
    mixer.value?.update(delta)
  })
})

const trackData = ref([])
const active = ref(1)
const progress = ref(0) // 进度条的进度，初始为 0
const startTime = computed(() => {
  return new Date(trackData.value[0]?.inJzTime).getTime() || null
})
const endTime = computed(() => {
  return new Date(trackData.value[trackData.value.length - 1]?.inJzTime).getTime() || null
})
const currentTime = computed(() => {
  return dayjs(new Date(startTime.value + (endTime.value - startTime.value) * progress.value)).format('YYYY-MM-DD HH:mm')
})
// 鼠标悬停时是否显示时间，初始为不显示
const showHoverTime = ref(false)
// 鼠标悬停位置对应的时间
const hoverTime = ref('')
// 存储鼠标位置的 x 坐标
const hoverX = ref(0)

const speed = ref(1)
function speedChange (val) {
  mixer.value.timeScale = val
  timeline.timeScale(val)
  if (playing.value) {
    stopPlay();
    startPlay();
  }
}

const options = [
  { value: 0.1, label: '0.1倍数' },
  { value: 0.5, label: '0.5倍数' },
  { value: 1, label: '正常' },
  { value: 2, label: '2倍' },
  { value: 4, label: '4倍' },
  { value: 8, label: '8倍' },
  { value: 16, label: '16倍' },
  { value: 32, label: '32倍' },
  { value: 64, label: '64倍' }
]
/**
 * 鼠标移动时显示对应位置的时间
 * @param event - 鼠标事件对象
 */
function showTimeOnHover (event: MouseEvent) {
  // 获取进度条容器的边界矩形
  const rect = (event.target as HTMLElement).getBoundingClientRect()
  // 计算鼠标在进度条容器内的 x 坐标
  const x = event.clientX - rect.left
  // 获取进度条容器的宽度
  const width = rect.width
  // 计算新的进度
  const newProgress = (x / width) * 100
  // 计算鼠标位置对应的时间
  const newTime = startTime.value + ((endTime.value - startTime.value) * newProgress / 100)
  // 更新鼠标悬停位置对应的时间
  hoverTime.value = dayjs(newTime).format('YYYY-MM-DD HH:mm')
  // 显示鼠标悬停时的时间标签
  showHoverTime.value = true
  // 更新鼠标位置的 x 坐标
  hoverX.value = event.offsetX
}
// 隐藏鼠标悬停时的时间标签
function hideTimeLabel () {
  showHoverTime.value = false
}
/**
 * 切换播放/暂停状态
 */
const playing = ref(false)
function playOrPause () {
  // 切换播放状态
  playing.value = !playing.value
  if (playing.value) {
    // 开始播放
    startPlay()
  } else {
    // 停止播放
    stopPlay()
  }
}

let isEnd = false
function startPlay () {
  if (isEnd) {
    timeline.progress(0)
    active.value = 0
  }
  playing.value = true;
  timeline.play()
  clip.value.play()
}

function stopPlay () {
  playing.value = false;
  timeline.pause()
  clip.value.stop()
}

/**
 * 点击进度条跳转时间
 * @param event - 鼠标事件对象
 */
function seek (event: MouseEvent) {
  let oldPlaying = playing.value;
  if (playing.value) {
    // 若正在播放，先停止播放
    stopPlay()
  }
  // 获取进度条容器的边界矩形
  const rect = (event.target as HTMLElement).getBoundingClientRect()
  // 计算鼠标在进度条容器内的 x 坐标
  const x = event.clientX - rect.left
  // 获取进度条容器的宽度
  const width = rect.width
  // 计算新的进度
  progress.value = x / width
  timeline.progress(progress.value)
  if (oldPlaying) {
    // 若之前处于播放状态，重新开始播放
    startPlay()
  }
}

onUnmounted(() => {
  stopPlay()
  peopleModel.value.remove(label.value)
  scene.value.remove(peopleModel.value)
  hideHistoryTrack()
})

const currentTrack = ref()
function createdTracklLabel () {
    const container = document.createElement('div')
    const vm = createVNode(BTrack, {
      info: currentTrack,
    }, null)
    render(vm, container)
    const label = new CSS2DObject(container.firstElementChild as Node)
    label.Vnode = vm
    return label
  }
</script>
<style scoped lang="scss">
.history-track {
  width: 100%;
  height: 100%;
  padding-left: 16px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.back-btn {
  color: var(--el-color-primary);
  margin-bottom: 16px;
  cursor: pointer;
  display: flex;
  align-items: center;
  font-size: 18px;

  .title {
    margin-left: 8px;
  }
}

.track-steps-container {
  flex: 1;
  overflow-y: auto;
}

:deep(.el-step__main) {
  padding-bottom: 20px;
}

.time-line {
  position: fixed;
  gap: 16px;
  left: 80px;
  bottom: 20px;
  display: flex;
  align-items: center;
  width: calc(100% - 160px - 300px);

  .progress-box {
    // 进度条容器的高度为 8px
    height: 8px;
    // 占据剩余的可用空间
    flex: 1;
    // 背景颜色，半透明绿色
    background-color: rgba(7, 187, 133, 0.1);
    // 相对定位，用于子元素的绝对定位
    position: relative;
    // 字体大小为 12px
    font-size: 12px;
    // 鼠标指针样式为手型
    cursor: pointer;

    .start-time {
      transform: translateX(-50%);
      position: absolute;
      bottom: -30px;
      left: 0;
      // 透明度为 0.5
      opacity: 0.5;
    }

    .end-time {
      // 绝对定位，位于容器顶部上方 30px，右侧 0 位置
      transform: translateX(50%);
      position: absolute;
      bottom: -30px;
      right: 0;
      // 透明度为 0.5
      opacity: 0.5;
    }

    .hover-time {
      // 绝对定位
      position: absolute;
      // 水平居中对齐
      transform: translateX(-50%);
      // 文本不换行
      white-space: nowrap;
    }

    .progress {
      // 禁止鼠标事件
      pointer-events: none;
      // 相对定位，用于子元素的绝对定位
      position: relative;
      // 背景颜色，Element Plus 主题色的浅色变体
      background-color: var(--el-color-primary-light-5);
      // 过渡动画，所有属性变化在 0.3 秒内完成，缓出效果
      transition: all 0.3s ease-out 0s;
      // 进度条高度为 8px
      height: 8px;
      // 初始宽度为 50%
      width: 50%;

      .time-label {
        // 显示元素
        display: block;
        // 绝对定位
        position: absolute;
        // 文本不换行
        white-space: nowrap;
        // 位于容器顶部上方 30px
        top: -30px;
        // 右侧对齐
        right: 0;
        // 水平向右偏移 50%
        transform: translateX(50%);
      }

      &::after {
        // 显示伪元素
        display: block;
        // 伪元素内容为空
        content: '';
        // 绝对定位
        position: absolute;
        // 位于容器顶部上方 4px
        top: -4px;
        // 右侧对齐
        right: 0;
        // 宽度为 4px
        width: 4px;
        // 高度为 16px
        height: 16px;
        // 背景颜色为白色
        background-color: #fff;
      }
    }

    .btn {
      // 按钮背景颜色，Element Plus 主题色的浅色变体
      background-color: var(--el-color-primary-light-5);
    }
  }

  .time-line-btn {
    // 图标字体大小为 24px
    font-size: 24px;
    // 图标颜色，Element Plus 主题色的浅色变体
    color: var(--el-color-primary-light-5);
    // 鼠标指针样式为手型
    cursor: pointer;
  }
}
</style>
